import jade.core.Actor;
import jade.core.World;
import jade.gen.Gen.GenFactory;
import jade.util.ColoredChar;
import jade.util.Coord;

import java.awt.Color;
import java.util.Collection;
import java.util.HashSet;
import java.util.Vector;

public class Level extends World {
	static final int LEVELS = 8;
	private final int ENEMIES = 10;
	private Dungeon dungeon;
	private transient PrisonEscape parent;

	final Sight[][] sight;
	private Player player;
	Collection<Enemy> enemies;
	private Coord entryCoord;
	private Coord exitCoord;

	Level(PrisonEscape parent, Dungeon dungeon, int depth, int levelWidth, int levelHeight) {
		super(levelWidth, levelHeight);
		this.dungeon = dungeon;
		this.parent = parent;

		sight = new Sight[levelHeight][levelWidth];
		for (int y = 0; y < levelHeight; y++)
			for (int x = 0; x < levelWidth; x++)
				sight[y][x] = Sight.UNSEEN;

		int algorithm = depth <= LEVELS / 2 ? GenFactory.Traditional
				: GenFactory.Cellular;
		GenFactory.get(algorithm).generate(this, dungeon.random.nextInt());

		entryCoord = depth > 1 ? getOpenTile(dungeon.random) : null;
		if (dungeon.parent.allowGoingBackUp)
			if (entryCoord != null)
				getTile(entryCoord).setTile('<', Color.blue, true);

		exitCoord = getOpenTile(dungeon.random);
		getTile(exitCoord).setTile('>', Color.green, true);

		enemies = new Vector<Enemy>();
		for (int e = 0; e < ENEMIES; e++) {
			Enemy enemy = new Enemy(parent, 'g', dungeon, Color.red, 1, 1, 1, 1);
			Coord enemyPos = getOpenTile(dungeon.random);
			enemies.add(enemy);
			addActor(enemy, enemyPos.getX(), enemyPos.getY());
		}
	}

	@Override
	public void addActor(Actor actor, int x, int y) {
		super.addActor(actor, x, y);
		if (actor instanceof Player) {
			player = (Player) actor;
			player.calcFoV();
		}
	}

	private void enemiesAct(boolean awake) {
		for (Enemy e : enemies)
			if (!e.isExpired()) {
				e.awake = awake;
				e.act();
			}
	}

	Coord getDownStairs() {
		return exitCoord;
	}

	Collection<Enemy> getEnemies() {
		return enemies;
	}

	@Override
	public ColoredChar getLook(int x, int y) {
		final Creature creature = getActorAt(x, y, Creature.class);
		if (creature != null)
			return creature.getLook();

		return super.getLook(x, y);
	}

	public Coord getOpenTile() {
		return getOpenTile(dungeon.random);
	}

	Player getPlayer() {
		return player;
	}

	Coord getUpStairs() {
		return entryCoord;
	}

	@Override
	public void removeExpired() {
		super.removeExpired();

		Collection<Enemy> expired = new HashSet<Enemy>();
		for (Enemy e : enemies)
			if (e.isExpired())
				expired.add(e);
		for (Enemy e : expired)
			enemies.remove(e);
	}

	@Override
	public void tick() {

		// Process player movment
		boolean playerMoved = player.actAndTell();
		if (playerMoved && !dungeon.parent.realTime)
			// In turn-based mode the enemies move after a player move
			for (Enemy e : enemies)
				e.move = true;

		// First turn after changing level the new enemies should not move
		if (player.changedLevel) {
			parent.updateScreen = true;
		}
		else if (!player.isExpired()) {
			enemiesAct(player.light);

			// Mark current field of view as "in sight"
			for (Coord c : player.fov)
				sight[c.getY()][c.getX()] = Sight.IN_SIGHT;

			removeExpired();
		}
	}
}
